﻿#include "CFormCtrl.h"

#include "MyHelper.h"
#include "QUIHelper.h"
#ifdef QT_NETWORK_LIB
#include <QHostAddress>
#endif

CMainWindow         *instance;

CTitleBar::CTitleBar(QWidget *parent, bool hasBtnMin, bool hasBtnMax)
    : QWidget(parent)
    , m_btnMin(hasBtnMin?new QPushButton(this):nullptr)
    , m_btnMax(hasBtnMax?new QPushButton(this):nullptr)
    , m_btnClose(new QPushButton(this))
    , m_isPressed(false)
    , m_mainView(parent)
    , m_labIco(new QLabel(this))
    , m_titleText(new QLabel(this))
    , m_navWidget(new QWidget(this))
    , m_navBtnGroup(nullptr)
    , m_layoutNavBtn(new QHBoxLayout(m_navWidget))
    , m_layoutRightBtn(new QHBoxLayout)
{
    Q_ASSERT_X(parent, "CTitleBar()", "CTitleBar类不允许父窗体为空");

    // 设置默认字体
    auto font = m_titleText->font();
    font.setBold(true);
    font.setPointSize(12);
    setTitleFont(font);

    this->setProperty("form", "title");
    // Qt中继承QWidget之后,样式表不起作用,必须加该句
    this->setAttribute(Qt::WA_StyledBackground, true);

    // 设置标题栏的icon和文本
    m_titleText->setText(this->parentWidget()->windowTitle());
    // 窗口icon图标不会轻易改变,无需关联信号槽
    if (!m_mainView->windowIcon().isNull()) {
        setTitleIcon(m_mainView->windowIcon(), QSize(26, 34));
    } else if (!qApp->windowIcon().isNull()) {
        setTitleIcon(qApp->windowIcon(), QSize(26, 34));
    }
    // 关联主界面的标题修改信号,让软件可动态修改标题
    connect(m_mainView, &QWidget::windowTitleChanged,
            this, [=](const QString &title) { m_titleText->setText(title); });
    connect(m_mainView, &QWidget::windowIconChanged,
            this, [=](const QIcon &icon) { setTitleIcon(icon, QSize(26, 34)); });

    if (m_btnMin) {
#ifdef USING_ONLY_CHINESE
        m_btnMin->setToolTip("最小化");
#else
        m_btnMin->setToolTip(tr("Minimize"));
#endif // USING_ONLY_CHINESE
        m_btnMin->setFixedSize(30, 30);
        m_btnMin->setObjectName("btnMenu_Min");
        m_btnMin->setDefault(false);
        m_btnMin->setFlat(true);

        // 设置Qt自带的按钮图标
        m_btnMin->setIcon(this->style()->standardIcon(QStyle::SP_TitleBarMinButton));
        // 关联信号槽
        connect(m_btnMin, &QAbstractButton::clicked, this->parentWidget(), &QWidget::showMinimized);
    }

    if (m_btnMax) {
#ifdef USING_ONLY_CHINESE
        m_btnMax->setToolTip("最大化");
#else
        m_btnMax->setToolTip(tr("Max"));
#endif // USING_ONLY_CHINESE
        m_btnMax->setFixedSize(30, 30);
        m_btnMax->setObjectName("btnMenu_Max");
        m_btnMax->setDefault(false);
        m_btnMax->setFlat(true);

        // 设置Qt自带的按钮图标
        m_btnMax->setIcon(this->style()->standardIcon(QStyle::SP_TitleBarMaxButton));
        // 关联信号槽
        connect(m_btnMax, &QAbstractButton::clicked, this, [=](){
            if (m_mainView->isMaximized()) {
                m_mainView->showNormal();
            } else {
                m_mainView->showMaximized();
            }
            checkParentWidgetIsMax();
        });
    }

    if (m_btnClose) {
#ifdef USING_ONLY_CHINESE
        m_btnClose->setToolTip("关闭");
#else
        m_btnClose->setToolTip(tr("Close"));
#endif // USING_ONLY_CHINESE
        m_btnClose->setFixedSize(30, 30);
        m_btnClose->setObjectName("btnMenu_Close");
        m_btnClose->setDefault(false);
        m_btnClose->setFlat(true);

        // 设置Qt自带的按钮图标
        m_btnClose->setIcon(this->style()->standardIcon(QStyle::SP_TitleBarCloseButton));
        // 关联信号槽
        connect(m_btnClose, &QAbstractButton::clicked, this->parentWidget(), &QWidget::close);
    }
    if (m_btnMin)   m_layoutRightBtn->addWidget(m_btnMin);
    if (m_btnMax)   m_layoutRightBtn->addWidget(m_btnMax);
    if (m_btnClose) m_layoutRightBtn->addWidget(m_btnClose);

    auto layoutRight = new QGridLayout;
    layoutRight->setSpacing(3);
    layoutRight->setContentsMargins(0, 0, 0, 0);
    layoutRight->addLayout(m_layoutRightBtn, 0, 0);
    layoutRight->setRowMinimumHeight(0, 30);
    layoutRight->setRowStretch(1, 1);

    // 设置弱属性以便应用样式
    m_navWidget->setProperty("nav", "top");
    // m_navWidget->setStyleSheet("background:qlineargradient(spread:pad,x1:0,y1:0,x2:0,y2:1,stop:0 #181818,stop:1 #181818);");
    m_layoutNavBtn->setSpacing(3);
    m_layoutNavBtn->setContentsMargins(0, 0, 0, 0);

    auto layoutMain = new QHBoxLayout(this);  // 主界面布局
    layoutMain->addWidget(m_labIco);
    layoutMain->addWidget(m_titleText);
    layoutMain->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
    layoutMain->addWidget(m_navWidget);
    layoutMain->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
    layoutMain->addLayout(layoutRight);

    layoutMain->setContentsMargins(9, 0, 0, 0);
    layoutMain->setSpacing(6);

    this->setMinimumHeight(30);
    // 延迟2ms判断界面show的时候, 是不是就是最大化
    QTimer::singleShot(2 , this, [=]() {
        checkParentWidgetIsMax();
    });
}

CTitleBar::~CTitleBar()
{
    if (m_navBtnGroup) {
        // 释放空间
        qDeleteAll(m_navBtnGroup->buttons());
        // 置为nullptr
        // m_navBtnGroup->buttons().clear();
        delete m_navBtnGroup;
    }

    auto btnList = this->findChildren <QPushButton*>();
    foreach (auto &item, btnList) {
        delete item;
    }

    auto labList = this->findChildren <QLabel*>();
    foreach (auto &item, labList) {
        delete item;
    }
}

void CTitleBar::setTitleBackground(const QColor &bgColor)
{
    if (!autoFillBackground()) {
        // 如果不加这个属性，填充颜色会不好用
        setAutoFillBackground(true);
    }

    // 填充颜色
    QPalette palette;
    palette.setColor(QPalette::Window, bgColor);
    // 添加自己的图片
    // palette.setBrush(QPalette::Background,QBrush(QPixmap(":/1.png")));
    setPalette(palette);
}

void CTitleBar::setTitleIcon(const QIcon &ico, const QSize &icoSize)
{
    QPixmap icon = ico.pixmap(icoSize);
    m_labIco->setFixedSize(icoSize);
    m_labIco->setPixmap(icon);
}

void CTitleBar::addNavLine()
{
    auto lineFrame = new QFrame(m_mainView);
    lineFrame->setObjectName("line");
    lineFrame->setFrameShape(QFrame::VLine);
    lineFrame->setFrameShadow(QFrame::Sunken);
    m_layoutNavBtn->addWidget(lineFrame);
}

QToolButton *CTitleBar::addNavButton(const QString &btnTxt, bool textBesideIcon, int minHight)
{
    if (!m_navBtnGroup) {
        m_navBtnGroup = new QButtonGroup(this);
    }
    auto toolButton = new QToolButton(this);
    m_navBtnGroup->addButton(toolButton);

    toolButton->setText(btnTxt);
    toolButton->setCheckable(true);
    toolButton->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    toolButton->setToolButtonStyle(textBesideIcon ? Qt::ToolButtonTextBesideIcon : Qt::ToolButtonTextUnderIcon);

    if (!m_navBtnGroup->checkedButton()) {
        m_navBtnGroup->buttons().at(0)->click();
    }

    m_layoutNavBtn->addWidget(toolButton);
    // 有导航按钮,标题栏高度增加
    this->setMinimumHeight(qMax(this->height(), minHight));
    return toolButton;
}

void CTitleBar::insertRightLine()
{
    auto lineFrame = new QFrame(m_mainView);
    lineFrame->setObjectName("line");
    lineFrame->setFrameShape(QFrame::VLine);
    lineFrame->setFrameShadow(QFrame::Sunken);
    m_layoutRightBtn->insertWidget(m_layoutRightBtn->count() - 3, lineFrame);
}

void CTitleBar::insertRightButton(QWidget *formCtrl)
{
    if (!formCtrl)
        return;

    m_layoutRightBtn->insertWidget(m_layoutRightBtn->count() - 3, formCtrl);
}

void CTitleBar::checkParentWidgetIsMax()
{
    if (!m_btnMax) return;

    // 设置Qt自带的按钮图标
    auto icon = m_mainView->isMaximized() ? this->style()->standardIcon(QStyle::SP_TitleBarNormalButton) :
                                            this->style()->standardIcon(QStyle::SP_TitleBarMaxButton);
    m_btnMax->setIcon(icon);
#ifdef USING_ONLY_CHINESE
    m_btnMax->setToolTip(m_mainView->isMaximized() ? "正常" : "最大化");
#else
    m_btnMax->setToolTip(m_mainView->isMaximized() ? tr("Normal") : tr("Max"));
#endif // USING_ONLY_CHINESE
}

void CTitleBar::setTitleFormat(CTitleBar *titleBar, QVBoxLayout *layoutMain, QWidget *widgetWorkspace)
{
    titleBar->setProperty("form", "title");

    Q_ASSERT_X(layoutMain, "CTitleBar::setTitleFormat",
               "标题栏的上层布局不可为空");
    // 格式化父窗体布局的边缘,让标题栏紧贴四个边
    layoutMain->setContentsMargins(0, 0, 0, 0);
    layoutMain->insertWidget(0, titleBar, 0);

    auto layout = new QVBoxLayout();
    layout->setContentsMargins(9, 9, 9, 9);
    layoutMain->addLayout(layout, 1);

    // 添加工作区
    Q_ASSERT_X(widgetWorkspace, "CTitleBar::addTitleWidget",
               "不能添加空的widget作为界面工作区");
    layout->addWidget(widgetWorkspace);
}

void CTitleBar::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton) {
        m_isPressed = true;
        // 获得鼠标的初始位置
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        m_mouseStartPoint = event->globalPosition().toPoint();
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        m_mouseStartPoint = event->globalPos();
#endif
    }
}

void CTitleBar::mouseMoveEvent(QMouseEvent *event)
{
    if (m_isPressed) {
        if (m_mainView->isFullScreen() && m_btnMax) {
            emit m_btnMax->clicked();
        }

        // 获得鼠标移动的距离
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        QPoint movePoint = event->globalPosition().toPoint() - m_mouseStartPoint;
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        QPoint movePoint = event->globalPos() - m_mouseStartPoint;
#endif
        QPoint widgetPos = m_mainView->pos();
        // 改变窗口的位置
        m_mainView->move(widgetPos + movePoint);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        m_mouseStartPoint = event->globalPosition().toPoint();
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        m_mouseStartPoint = event->globalPos();
#endif
    }
}

void CTitleBar::mouseReleaseEvent(QMouseEvent *event)
{
    Q_UNUSED(event)
    m_isPressed = false;
}

CWidget::CWidget(QWidget *parent)
    : QWidget(parent)
    , m_titleBar(nullptr)
{
    // 此处调用了css中的窗体边框
    setProperty("form", true);
    // Qt中继承QWidget之后,样式表不起作用,必须加该句
    this->setAttribute(Qt::WA_StyledBackground, true);

    // 设置随主窗口关闭而关闭
    this->setAttribute(Qt::WA_QuitOnClose, false);
}

CWidget::~CWidget()
{
    if (!m_titleBar)
        delete m_titleBar;

    m_tableWidgetAutoList.clear();
#if defined (MOUSE_MOVE_ENABLED_WIDGET) || defined (MOUSE_MOVE_ENABLED_MAINWINDOW)
    m_ctrlList.clear();
#endif
}

void CWidget::initializeComponent()
{
#if defined (MOUSE_MOVE_ENABLED_WIDGET) || defined (MOUSE_MOVE_ENABLED_MAINWINDOW)
    // 把所有的下拉框加入过滤，防止点击时，窗口位置发生偏移
    m_ctrlList = CWidget::getAllComboAndMenuCtrl(this);
#endif

    // 设置窗体图标(软件图标和主窗体图标2选1,优先主窗体图标)
    if (!qApp->windowIcon().isNull()) {
        setWindowIcon(qApp->windowIcon());
    }
    if (!instance->windowIcon().isNull()) {
        setWindowIcon(instance->windowIcon());
    }
}

void CWidget::addTitleBar(bool hasBtnMax, QWidget *widgetWorkspace)
{
    Q_ASSERT_X(!m_titleBar, "CWidget::addTitleBar",
               "不可重复添加标题栏");
    QUIHelper::setNoTitle(this);
    // 可移动的标题栏
    m_titleBar = new CTitleBar(this, true, hasBtnMax);

    QVBoxLayout *layoutMain;
    if (this->layout()) {
        Q_ASSERT_X(this->layout()->inherits("QVBoxLayout"), "CWidget::addTitleBar",
                   "标题栏的上层布局必须是QVBoxLayout");
        layoutMain = static_cast<QVBoxLayout*>(this->layout());
    } else {
        layoutMain = new QVBoxLayout(this);
    }

    CTitleBar::setTitleFormat(m_titleBar, layoutMain, widgetWorkspace);
}

void CWidget::setTableWidgetAutoColWidth(QTableWidget *tableWidget, int widestColId, int defColWidth)
{
    m_tableWidgetAutoList.append(std::tuple<QTableWidget*, int , int>(tableWidget, widestColId, defColWidth));
    QTimer::singleShot(2 , this, [=]() {
        resizeEvent(new QResizeEvent(this->size() , this->size()));
    });
}

void CWidget::resizeEvent(QResizeEvent *event)
{
    QUIHelper::setCenter(this);

    if (!m_tableWidgetAutoList.isEmpty()) {
        for (auto item : m_tableWidgetAutoList) {
            CWidget::autoTableWidgetColWidth(std::get<0>(item), std::get<1>(item), std::get<2>(item));
        }
    }

    QWidget::resizeEvent(event);
}

#ifdef MOUSE_MOVE_ENABLED_WIDGET
void CWidget::mousePressEvent(QMouseEvent *event)
{
    // 有父窗体不移动
    if (this->parentWidget()) {
        return;
    }

    if(event->button() == Qt::LeftButton) {
        m_isPressed = true;
        // 获得鼠标的初始位置
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        m_mouseStartPoint = event->globalPosition().toPoint();
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        m_mouseStartPoint = event->globalPos();
#endif
    }
}

void CWidget::mouseMoveEvent(QMouseEvent *event)
{
    if(m_isPressed) {
        // 获得鼠标移动的距离
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        QPoint movePoint = event->globalPosition().toPoint() - m_mouseStartPoint;
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        QPoint movePoint = event->globalPos() - m_mouseStartPoint;
#endif
        QPoint widgetPos = this->pos();
        // 改变窗口的位置
        this->move(widgetPos + movePoint);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        m_mouseStartPoint = event->globalPosition().toPoint();
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        m_mouseStartPoint = event->globalPos();
#endif
    }
}

void CWidget::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton) {
        m_isPressed = false;
    }
}
#endif
#if defined (MOUSE_MOVE_ENABLED_WIDGET) || defined (MOUSE_MOVE_ENABLED_MAINWINDOW)
bool CWidget::eventFilter(QObject *obj, QEvent *event)
{
    if (m_ctrlList.contains(static_cast<QWidget*>(obj))) {
        if(event->type() == QEvent::MouseMove){
            return true;
        }
    }
    return QWidget::eventFilter(obj, event);
}
#endif

QList<QWidget *> CWidget::getAllComboAndMenuCtrl(QWidget *parent)
{
    QList<QWidget *> ctrlList;
    QList<QComboBox*> comboBoxList = parent->findChildren<QComboBox*>();
    foreach (auto &comboBox, comboBoxList) {
        comboBox->installEventFilter(parent);
        ctrlList.append(comboBox);
    }

    // 把所有的菜单加入过滤，防止点击时，窗口发生偏移
    QList<QMenu*> menuList = parent->findChildren<QMenu*>();
    foreach (auto &menu, menuList) {
        menu->installEventFilter(parent);
        ctrlList.append(menu);
    }
    return ctrlList;
}

void CWidget::autoTableWidgetColWidth(QTableWidget *tableWidget, int widestColId, int defColWidth)
{
    for (int i = 0; i < tableWidget->columnCount(); ++i) {
        tableWidget->setColumnWidth(i, defColWidth);
    }

    if (widestColId == tableWidget->columnCount() - 1) {
        return;
    }

    tableWidget->setColumnWidth(widestColId, tableWidget->columnWidth(tableWidget->columnCount() - 1));
    for (int i = 0; i < tableWidget->columnCount(); ++i) {
        if (i != widestColId) {
            tableWidget->setColumnWidth(i, defColWidth);
        }
    }
}

CMainWindow::CMainWindow(QWidget *parent)
    : QMainWindow(parent)
    , m_settings(new QSettings(qAppName() + ".config.ini", QSettings::IniFormat))
    , m_titleBar(nullptr)
    , m_toolBar(nullptr)
    , m_sharedMemory(nullptr)
{
    instance = this;

#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    // 在此添加编码格式设置（如果INI文件编码为UTF-8则这里设置UTF-8，如果为GB2312，则这里设置GB2312，两者一致）
    // 即可读写ini文件中的中文
    m_settings->setIniCodec(QTextCodec::codecForName("UTF-8"));
#endif

    // 此处调用了css中的窗体边框
    setProperty("form", true);
    // 设置窗体图标
    qApp->setWindowIcon(QIcon(":/icon.ico"));
}

CMainWindow::~CMainWindow()
{
    if (!m_titleBar)
        delete m_titleBar;
    if (!m_toolBar)
        delete m_toolBar;
    if (!m_settings)
        delete m_settings;

    if (!m_sharedMemory)
        delete m_sharedMemory;

    m_tableWidgetAutoList.clear();
#ifdef MOUSE_MOVE_ENABLED_MAINWINDOW
    m_ctrlList.clear();
#endif
}

void CMainWindow::setAppSingleton()
{
    // **利用共享内存判断程序是否重复启动
    m_sharedMemory = new QSharedMemory(qAppName());
    if (m_sharedMemory->attach()) {
        if (!m_sharedMemory->detach()) {
#ifdef USING_ONLY_CHINESE
            qDebug().noquote() << m_sharedMemory->key() << "共享内存分离失败！";
            CDialog::MessageBoxError(m_sharedMemory->key() + "共享内存分离失败！");
#else
            qDebug().noquote() << m_sharedMemory->key() << "detach failed!";
            CDialog::MessageBoxError(m_sharedMemory->key() + tr("detach failed!"));
#endif // USING_ONLY_CHINESE
            QTimer::singleShot(5 , this, &QMainWindow::close);
        }
    }
    if (!m_sharedMemory->create(1)) {
#ifdef Q_OS_LINUX
        qInfo() << m_sharedMemory->key() << "already exists, please use ps -ef | grep DisorderFetch3D to check!";
#endif
#ifdef Q_OS_WIN
        qInfo() << m_sharedMemory->key() << "already exists, the system can not open more!";
#endif
        qInfo() << "sharedMemory error type: " << m_sharedMemory->error() << m_sharedMemory->isAttached();
        qInfo() << "sharedMemory ERROR: " << m_sharedMemory->errorString();
#ifdef Q_OS_LINUX
        CDialog::MessageBoxError(m_sharedMemory->key() + tr(" exist, please use ps -ef | grep DisorderFetch3D to check!"));
#endif
#ifdef Q_OS_WIN
#ifdef USING_ONLY_CHINESE
        CDialog::MessageBoxError(m_sharedMemory->key() + " 已经启动，本软件不允许多开！");
#else
        CDialog::MessageBoxError(m_sharedMemory->key() + tr(" already exists, the system can not open more!"));
#endif // USING_ONLY_CHINESE
#endif
        // 若软件已启动,5ms后退出,防止重复启动
        QTimer::singleShot(5, this, &QMainWindow::close);
    }
}

void CMainWindow::initializeComponent()
{
    this->installEventFilter(this);
    auto txtList = findChildren<QWidget*>();
    foreach (auto &item, txtList) {
        item->installEventFilter(this);
    }

#ifdef MOUSE_MOVE_ENABLED_MAINWINDOW
    // 把所有的下拉框加入过滤，防止点击时，窗口位置发生偏移
    m_ctrlList = CWidget::getAllComboAndMenuCtrl(this);
#endif
}

void CMainWindow::resetIconAndTitle()
{
    // 设置窗体图标
    if (QFile("icon.ico").exists()) {
        this->setWindowIcon(QIcon("icon.ico"));
    }
    // 设置窗体标题
    auto titleTxt = m_settings->value("Global/CompanyName").toString();
    if (titleTxt.isEmpty()) {
        titleTxt = "南京桂瑞得信息科技有限公司";
        m_settings->setValue("Global/CompanyName", titleTxt);
    }
    this->setWindowTitle(titleTxt);
}

void CMainWindow::addTitleBar(bool hasBtnMax, QWidget *widgetWorkspace)
{
    Q_ASSERT_X(!m_titleBar, "CMainWindow::addTitleBar",
               "不可重复添加标题栏");
    QUIHelper::setNoTitle(this);
    // 可移动的标题栏
    m_titleBar = new CTitleBar(this, true, hasBtnMax);

    QVBoxLayout *layoutMain;
    if (this->centralWidget()->layout()) {
        Q_ASSERT_X(this->centralWidget()->layout()->inherits("QVBoxLayout"), "CMainWindow::addTitleBar",
                   "标题栏的上层布局必须是QVBoxLayout");
        layoutMain = static_cast<QVBoxLayout*>(this->centralWidget()->layout());
    } else {
        layoutMain = new QVBoxLayout(this->centralWidget());
    }

    CTitleBar::setTitleFormat(m_titleBar, layoutMain, widgetWorkspace);
}

void CMainWindow::addToolBarTop()
{
    // 如果添加工具栏,则不能有自定义标题栏
    Q_ASSERT_X(m_titleBar, "CMainWindow::addToolBarTop",
               "如果添加工具栏,则不能有自定义标题栏");

    if (!m_toolBar) {
        // 设置工具栏
        m_toolBar = new QToolBar();
        this->addToolBar(Qt::TopToolBarArea, m_toolBar);
        // 只允许左右停靠工具栏
        m_toolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::LeftToolBarArea);
        // 设置工具栏可移动
        m_toolBar->setMovable(false);
    }
}

QComboBox *CMainWindow::addComboBoxSkin(bool isChinese)
{
    auto comSkinStyle = new QComboBox(this);
#ifdef USING_ONLY_CHINESE
    comSkinStyle->setToolTip("软件皮肤");
#else
    comSkinStyle->setToolTip(tr("Skin"));
#endif // USING_ONLY_CHINESE
    comSkinStyle->setMinimumWidth(100);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    connect(comSkinStyle, &QComboBox::activated, this, [=](int index) {
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    connect(comSkinStyle, QOverload<int>::of(&QComboBox::activated), this, [=](int index) {
#endif
        QUIHelper::setSkinIndex(index);
        m_settings->setValue("Global/Skin", comSkinStyle->currentText());
    });

    auto qssInfoList = QUIHelper::getSkinInfoList();
    for (int i = 0; i < qssInfoList.size(); ++i) {
        if (isChinese) {
            comSkinStyle->addItem(std::get<2>(qssInfoList[i]), std::get<1>(qssInfoList[i]));
        } else {
            comSkinStyle->addItem(std::get<2>(qssInfoList[i]), std::get<0>(qssInfoList[i]));
        }
    }

    auto strSkin = m_settings->value("Global/Skin").toString();
    if (strSkin.isEmpty()) {
        strSkin = comSkinStyle->currentText();
        emit comSkinStyle->activated(0);
    }
    comSkinStyle->setCurrentText(strSkin);
    QUIHelper::setSkinIndex(comSkinStyle->currentIndex());

    return comSkinStyle;
}

QPushButton *CMainWindow::addHelpPdfButton()
{
    auto btnHelpPDF = new QPushButton(this);
    btnHelpPDF->setIcon(this->style()->standardIcon(QStyle::SP_MessageBoxQuestion));// 设置Qt自带的按钮图标
#ifdef USING_ONLY_CHINESE
    btnHelpPDF->setToolTip("帮助文档");
#else
    btnHelpPDF->setToolTip(tr("Help document"));
#endif // USING_ONLY_CHINESE
    return btnHelpPDF;
}

void CMainWindow::setTableWidgetAutoColWidth(QTableWidget *tableWidget, int widestColId, int defColWidth)
{
    m_tableWidgetAutoList.append(std::tuple<QTableWidget*, int , int>(tableWidget, widestColId, defColWidth));
    QTimer::singleShot(2 , this, [=]() {
        resizeEvent(new QResizeEvent(this->size() , this->size()));
    });
}

void CMainWindow::resizeEvent(QResizeEvent *event)
{
    if (!m_tableWidgetAutoList.isEmpty()) {
        for (auto item : m_tableWidgetAutoList) {
            CWidget::autoTableWidgetColWidth(std::get<0>(item), std::get<1>(item), std::get<2>(item));
        }
    }

    QMainWindow::resizeEvent(event);
}

bool CMainWindow::eventFilter(QObject *obj, QEvent *event)
{
#ifdef MOUSE_MOVE_ENABLED_MAINWINDOW
    if (m_ctrlList.contains(static_cast<QWidget*>(obj))) {
        if(event->type() == QEvent::MouseMove){
            return true;
        }
    }
#endif

    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *eventKey = static_cast<QKeyEvent*>(event);
        if (eventKey->modifiers() == (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier) && eventKey->key() == Qt::Key_U) {
#ifdef USING_ONLY_CHINESE
            LOG << ">>> 启用调试人员助手";
#else
            LOG << tr(">>> Enable Debugger Assistant");
#endif // USING_ONLY_CHINESE
            debugShortcut();

            return true;
        }

        if (eventKey->modifiers() == (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier) && eventKey->key() == Qt::Key_P) {
#ifdef USING_ONLY_CHINESE
            LOG << ">>> 启用超级管理员";
#else
            LOG << tr(">>> Enable Super Administrator");
#endif // USING_ONLY_CHINESE
            superShortcut();

            return true;
        }
    }

    return QMainWindow::eventFilter(obj, event);
}

#ifdef MOUSE_MOVE_ENABLED_MAINWINDOW
void CMainWindow::mousePressEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton) {
        m_isPressed = true;
        // 获得鼠标的初始位置
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        m_mouseStartPoint = event->globalPosition().toPoint();
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        m_mouseStartPoint = event->globalPos();
#endif
    }
}

void CMainWindow::mouseMoveEvent(QMouseEvent *event)
{
    if(m_isPressed) {
        // 获得鼠标移动的距离
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        QPoint movePoint = event->globalPosition().toPoint() - m_mouseStartPoint;
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        QPoint movePoint = event->globalPos() - m_mouseStartPoint;
#endif
        QPoint widgetPos = this->pos();
        // 改变窗口的位置
        this->move(widgetPos + movePoint);
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        m_mouseStartPoint = event->globalPosition().toPoint();
#elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
        m_mouseStartPoint = event->globalPos();
#endif
    }
}

void CMainWindow::mouseReleaseEvent(QMouseEvent *event)
{
    if(event->button() == Qt::LeftButton) {
        m_isPressed = false;
    }
}
#endif

void CMainWindow::onShowMessage(bool isDlg, bool isInfoOrErr, const QString &msgTitle, const QString &msgData, int timeout_ms)
{
    if (isDlg) {
        if (isInfoOrErr) {
#ifdef USING_ONLY_CHINESE
            CDialog::MessageBoxInfo((msgTitle.isEmpty() ? "提示" : msgTitle), msgData);
#else
            CDialog::MessageBoxInfo((msgTitle.isEmpty() ? tr("Tips") : msgTitle), msgData);
#endif // USING_ONLY_CHINESE
        } else {
#ifdef USING_ONLY_CHINESE
            CDialog::MessageBoxError((msgTitle.isEmpty() ? "错误" : msgTitle), msgData);
#else
            CDialog::MessageBoxError((msgTitle.isEmpty() ? tr("Tips") : msgTitle), msgData);
#endif // USING_ONLY_CHINESE
        }
    } else {
        statusBar()->showMessage(msgData, timeout_ms);
    }
}

CDialog::CDialog(QWidget *parent)
    : QDialog(parent)
    , m_closeSec(0)
    , m_titleBar(nullptr)
    , m_layoutContent(nullptr)
    , m_labMsg(nullptr)
    , m_btnGroup(nullptr)
{
    Q_ASSERT_X(parent, "CDialog()", "CDialog类不允许父窗体为空");
    setWindowTitle(qAppName());

    // 此处调用了css中的窗体边框
    setProperty("form", true);
    // 设置窗体图标(软件图标和主窗体图标2选1,优先主窗体图标)
    if (!qApp->windowIcon().isNull()) {
        setWindowIcon(qApp->windowIcon());
    }
    if (!instance->windowIcon().isNull()) {
        setWindowIcon(instance->windowIcon());
    }
}

CDialog::~CDialog()
{
    delete m_titleBar;
    if (m_layoutContent) {
        delete m_layoutContent;
    }
    if (m_labMsg) {
        delete m_labMsg;
    }
    if (m_btnGroup) {
        // 释放空间
        qDeleteAll(m_btnGroup->buttons());
        // 置为nullptr
        // m_btnGroup->buttons().clear();
        delete m_btnGroup;
    }
}

void CDialog::MessageBoxInfo(const QString &title, const QString &text, const QString &btnText, QMessageBox::StandardButtons btnType)
{
    (new CDialog(instance))->MessageBoxEx_Ex(QMessageBox::Information, title, text, btnType, QStringList(btnText), 0, 0);
}

void CDialog::MessageBoxError(const QString &title, const QString &text, const QString &btnText, QMessageBox::StandardButtons btnType)
{
    (new CDialog(instance))->MessageBoxEx_Ex(QMessageBox::Critical, title, text, btnType, QStringList(btnText), 0, 0);
}

int CDialog::MessageBoxWaiting(const QString &title, const QString &text, int defaultBtnId, int closeSec,
                               const QString &btn1txt, const QString &btn2txt, QMessageBox::StandardButtons btnType)
{
    return (new CDialog(instance))->MessageBoxEx_Ex(QMessageBox::NoIcon, title, text, btnType, QStringList() << btn1txt << btn2txt, defaultBtnId, closeSec);
}

int CDialog::MessageBoxQuestion(const QString &title, const QString &text, const QString &btn1txt, const QString &btn2txt)
{
    return (new CDialog(instance))->MessageBoxEx_Ex(QMessageBox::Question, title, text, QMessageBox::Ok | QMessageBox::Cancel, QStringList() << btn1txt << btn2txt, 0, 0);
}

int CDialog::MessageBoxQuestions(const QString &title, const QString &text, const QStringList &btnListTxt)
{
    return (new CDialog(instance))->MessageBoxEx_Ex(QMessageBox::Warning, title, text, QMessageBox::Save, btnListTxt, 0, 0);
}

QString CDialog::getText(const QString &title, const QString &label, QLineEdit::EchoMode echo, const QString &text, bool *ok)
{
    auto dlg = new CDialog(instance);
    auto widget = dlg->MessageInputBox(new QLineEdit(dlg), title, label);
    auto lineEdit = static_cast<QLineEdit*>(widget);
    lineEdit->setEchoMode(echo);
    lineEdit->setText(text);

    if (dlg->exec() == QDialog::Accepted) {
        if (ok) {
            *ok = true;
        }
        return lineEdit->text();
    } else {
        if (ok) {
            *ok = false;
        }
        return text;
    }
}

QString CDialog::getItem(const QString &title, const QString &label, const QStringList &items, int current, bool *ok)
{
    Q_ASSERT_X(current < items.size(), "CDialog::getItem", "List的下标应小于List的总量！");
    auto dlg = new CDialog(instance);
    auto widget = dlg->MessageInputBox(new QComboBox(dlg), title, label);
    auto comboBox = static_cast<QComboBox*>(widget);
    comboBox->addItems(items);
    comboBox->setCurrentIndex(current);

    if (dlg->exec() == QDialog::Accepted) {
        if (ok) {
            *ok = true;
        }
        return comboBox->currentText();
    } else {
        if (ok) {
            *ok = false;
        }
        return comboBox->itemText(current);
    }
}

int CDialog::getInt(const QString &title, const QString &label, int value, int minValue, int maxValue, int step, bool *ok)
{
    auto dlg = new CDialog(instance);
    auto widget = dlg->MessageInputBox(new QSpinBox(dlg), title, label);
    auto spinBox = static_cast<QSpinBox*>(widget);
    spinBox->setRange(minValue, maxValue);
    spinBox->setValue(value);
    spinBox->setSingleStep(step);

    if (dlg->exec() == QDialog::Accepted) {
        if (ok) {
            *ok = true;
        }
        return spinBox->value();
    } else {
        if (ok) {
            *ok = false;
        }
        return value;
    }
}

double CDialog::getDouble(const QString &title, const QString &label, double value, double minValue, double maxValue, int decimals, bool *ok, double step)
{
    auto dlg = new CDialog(instance);
    auto widget = dlg->MessageInputBox(new QDoubleSpinBox(dlg), title, label);
    auto doubleSpinBox = static_cast<QDoubleSpinBox*>(widget);
    doubleSpinBox->setRange(minValue, maxValue);
    doubleSpinBox->setValue(value);
    doubleSpinBox->setSingleStep(step);
    doubleSpinBox->setDecimals(decimals);

    if (dlg->exec() == QDialog::Accepted) {
        if (ok) {
            *ok = true;
        }
        return doubleSpinBox->value();
    } else {
        if (ok) {
            *ok = false;
        }
        return value;
    }
}

void CDialog::addTitleBar(QWidget *widgetWorkspace)
{
    Q_ASSERT_X(!m_titleBar, "CDialog::addTitleBar", "不可重复添加标题栏");
    QUIHelper::setNoTitle(this);
    // 可移动的标题栏
    m_titleBar = new CTitleBar(this, false, false);

    // 关闭按钮
    // 关联信号槽(如果是等待对话框,用DialogResult来返回按钮结果
    connect(m_titleBar->m_btnClose, &QAbstractButton::clicked, this, [=](){ done(0); });

    // frame的作用是为了使qss的配置生效
    auto frameContent = new QFrame(this);
    m_layoutContent = new QVBoxLayout(frameContent);
    if (widgetWorkspace) {
        m_layoutContent->addWidget(widgetWorkspace);
    }

    QVBoxLayout *layoutMain = new QVBoxLayout(this);
    CTitleBar::setTitleFormat(m_titleBar, layoutMain, frameContent);
}

void CDialog::initializeComponent()
{
#ifdef __arm__
    setMinimumWidth(300);
    // resize(300, 160);
#else
    setMinimumWidth(280);
    // setMaximumWidth(480);
    // resize(280, 160);
#endif

    this->raise();
}

void CDialog::onTimeout()
{
    if (m_closeSec > 0) {
        m_closeSec--;

        if (m_labMsg) {
            QStringList msg_list = m_labMsg->text().split(NEWLINE);
            msg_list.removeLast();
            msg_list.append(tr("-- Countdown: %1s").arg(m_closeSec));
            m_labMsg->setText(msg_list.join(NEWLINE));
        }
    } else {
        // 计时结束，默认点击ok键盘
        // this->accept();
        this->findChild<QPushButton*>("btnDefault")->click();
    }
}

int CDialog::MessageBoxEx_Ex(QMessageBox::Icon msgType, const QString &title, const QString &text, QMessageBox::StandardButtons btnType,
                             const QStringList &btnsTxt, int defaultBtnId, int closeSec)
{
    // 自我初始化
    CDialog::initializeComponent();
    addTitleBar(nullptr);

    // 1, 设置标题文本, 定义图标和消息 ----------------
    setLabsImgAndTxt(msgType, title);
    // 2, 定义和设置按钮文本 ----------------
    setButtonsText(btnType, btnsTxt, defaultBtnId);

    // 3, 设置等待对话框的定时器 ----------------
    if (msgType == QMessageBox::NoIcon) {
        // 设置消息文本
        m_labMsg->setText(text + NEWLINE + tr("-- Countdown: %1s").arg(closeSec));
        // 启动倒计时
        m_closeSec = closeSec;
        QTimer *timer = new QTimer(this);
        timer->setInterval(1000);
        connect(timer, &QTimer::timeout, this, &CDialog::onTimeout);
        timer->start();
    } else {
        // 设置消息文本
        m_labMsg->setText(text);
        // beep一下发声提醒
        QApplication::beep();

    }
    this->raise();

    int ret = this->exec();
    delete this;        // 释放对话框内存
    return ret;
}

QWidget *CDialog::MessageInputBox(QWidget *widget, const QString &title, const QString &label)
{
    setWindowTitle(title);
    QUIHelper::setNoTitle(this);

    // 可移动的标题栏
    auto titleBar = new CTitleBar(this, false, false);
    titleBar->setProperty("form", "title");
    // 关闭按钮
    // 关联信号槽(如果是等待对话框,用DialogResult来返回按钮结果
    connect(titleBar->m_btnClose, &QAbstractButton::clicked, this, [=](){ done(0); });

    QVBoxLayout *layoutMain = new QVBoxLayout(this);
    layoutMain->setContentsMargins(0, 0, 0, 0);
    layoutMain->setSpacing(0);
    layoutMain->insertWidget(0, titleBar, 0);

    auto layout = new QVBoxLayout();
    layout->setContentsMargins(9, 9, 9, 9);
    layout->setSpacing(6);
    layoutMain->addLayout(layout);

    // msg内容Label
    auto labMsg = new QLabel(this);
    labMsg->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    labMsg->setMinimumHeight(33);
    labMsg->setScaledContents(false);
    labMsg->setText(label);
    layout->addWidget(labMsg);

    if (widget) {
        layout->addWidget(widget);
    }

    auto buttonBox = new QDialogButtonBox(this);
    buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
#ifdef USING_ONLY_CHINESE
    buttonBox->button(QDialogButtonBox::Ok)->setText("确定");
    buttonBox->button(QDialogButtonBox::Cancel)->setText("取消");
#else
    buttonBox->button(QDialogButtonBox::Ok)->setText("Ok");
    buttonBox->button(QDialogButtonBox::Cancel)->setText("Cancel");
#endif
    buttonBox->button(QDialogButtonBox::Ok)->setDefault(true);
    // 关联信号槽(如果是等待对话框,用DialogResult来返回按钮结果
    connect(buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::clicked,
            this, [=](){ done(1); });
    connect(buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::clicked,
            this, [=](){ done(0); });
    layout->addWidget(buttonBox);

    return widget;
}

void CDialog::setLabsImgAndTxt(QMessageBox::Icon msgType, const QString &title)
{
    // msg图标Label
    QLabel *labIcon = new QLabel(this);
    labIcon->setFixedSize(30, 30);

    // 根据对话框类型设置图标和默认的窗口标题
    switch (msgType) {
    case QMessageBox::NoIcon:
        labIcon->setStyleSheet("");
        labIcon->setFixedSize(10, 10);
#ifdef USING_ONLY_CHINESE
        setWindowTitle("等待中...");
#else
        setWindowTitle(tr("Waiting..."));
#endif // USING_ONLY_CHINESE
        break;

    case QMessageBox::Information:
        labIcon->setStyleSheet("border-image: url(:/CFormContrl/msg_info.png);");
#ifdef USING_ONLY_CHINESE
        setWindowTitle("提示");
#else
        setWindowTitle(tr("Tips"));
#endif // USING_ONLY_CHINESE
        break;

    case QMessageBox::Critical:
        labIcon->setStyleSheet("border-image: url(:/CFormContrl/msg_error.png);");
#ifdef USING_ONLY_CHINESE
        setWindowTitle("错误");
#else
        setWindowTitle(tr("Error"));
#endif // USING_ONLY_CHINESE
        break;

    case QMessageBox::Question:
        labIcon->setStyleSheet("border-image: url(:/CFormContrl/msg_question.png);");
#ifdef USING_ONLY_CHINESE
        setWindowTitle("问询");
#else
        setWindowTitle(tr("Question"));
#endif // USING_ONLY_CHINESE
        break;

    default:
        break;
    }
    // 设置标题栏名字
    if (!title.isEmpty()) {
        setWindowTitle(title);
    }

    // msg内容Label
    m_labMsg = new QLabel(this);
    m_labMsg->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    m_labMsg->setMinimumHeight(33);
    m_labMsg->setScaledContents(false);

    // msg位置的布局
    auto layoutMsg = new QHBoxLayout();
    layoutMsg->setContentsMargins(0, 0, 0, 0);
    layoutMsg->addWidget(labIcon);
    layoutMsg->addWidget(m_labMsg);
    layoutMsg->addItem(new QSpacerItem(5, 0, QSizePolicy::Minimum, QSizePolicy::Minimum));
    // 设置显示内容
    m_layoutContent->insertLayout(0, layoutMsg, 1);
}

void CDialog::setButtonsText(QMessageBox::StandardButtons btnType, const QStringList &btnsTxt, int defaultBtnId)
{
    m_btnGroup = new QButtonGroup(this);
    // 1, 增加按钮
    if ((btnType & QMessageBox::Ok) || (btnType & QMessageBox::Yes)) {
        auto btn = new QPushButton(this);
        QString txt = nullptr;
        if (!btnsTxt.empty()) {
            txt = btnsTxt.at(0);
        }

        if (!txt.isEmpty()) {
            btn->setText(txt);
        } else if (btnType & QMessageBox::Ok) {
#ifdef USING_ONLY_CHINESE
            btn->setText("确认");
#else
            btn->setText(tr("Ok"));
#endif // USING_ONLY_CHINESE
        } else {
#ifdef USING_ONLY_CHINESE
            btn->setText("是");
#else
            btn->setText(tr("Yes"));
#endif // USING_ONLY_CHINESE
        }

        btn->setIcon(QIcon(":/CFormContrl/btn_ok.png"));
        m_btnGroup->addButton(btn, 1);
    }

    if ((btnType & QMessageBox::Cancel) || (btnType & QMessageBox::No)) {
        auto btn = new QPushButton(this);
        QString txt = nullptr;
        if (m_btnGroup->buttons().size() == 0) {
            if (!btnsTxt.empty()) {
                txt = btnsTxt.at(0);
            }
        } else if (m_btnGroup->buttons().size() == 1) {
            if (btnsTxt.size() >= 2) {
                txt = btnsTxt.at(1);
            }
        }

        if (!txt.isEmpty()) {
            btn->setText(txt);
        } else if (btnType & QMessageBox::Cancel) {
#ifdef USING_ONLY_CHINESE
            btn->setText("取消");
#else
            btn->setText(tr("Cancel"));
#endif // USING_ONLY_CHINESE
        } else {
#ifdef USING_ONLY_CHINESE
            btn->setText("否");
#else
            btn->setText(tr("No"));
#endif // USING_ONLY_CHINESE
        }

        btn->setIcon(QIcon(":/CFormContrl/btn_close.png"));
        // 对应键盘上面的Esc
        btn->setShortcut(Qt::Key_Escape);
        m_btnGroup->addButton(btn, 0);
    }

    if (m_btnGroup->buttons().empty()) {
        for (int i = 0; i < btnsTxt.size(); ++i) {
            m_btnGroup->addButton(new QPushButton(btnsTxt.at(i), this), i);
        }
    }

    // 2, 给按钮布局
    auto layoutBtn = new QHBoxLayout;
    layoutBtn->setContentsMargins(0, 0, 0, 0);
    layoutBtn->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
    foreach (auto &btn, m_btnGroup->buttons()) {
        btn->setMinimumWidth(80);
        btn->setFocusPolicy(Qt::StrongFocus);
        btn->setIconSize(QSize(18, 18));
        layoutBtn->addWidget(btn);
    }
    m_layoutContent->addLayout(layoutBtn);

    // 3, 关联按钮信号
    connect(m_btnGroup, &QButtonGroup::idClicked, this, [=](int id){ done(id); });

    // 4, 设置默认按钮
    if (m_btnGroup->buttons().size() > defaultBtnId) {
        for (int i = 0; i < m_btnGroup->buttons().size(); ++i) {
            if (defaultBtnId == i) {
                m_btnGroup->buttons().at(i)->setObjectName("btnDefault");
                static_cast<QPushButton*>(m_btnGroup->buttons().at(i))->setDefault(true);
                break;
            }
        }
    } else if (!m_btnGroup->buttons().empty()) {
        m_btnGroup->buttons().at(0)->setObjectName("btnDefault");
        static_cast<QPushButton*>(m_btnGroup->buttons().at(0))->setDefault(true);
    }
}

void CDialog::resizeEvent(QResizeEvent *event)
{
    QUIHelper::setCenter(this);
    QDialog::resizeEvent(event);
}

QValidator::State CustomIntValidator::validate(QString &s, int &n) const
{
    if (s.isEmpty()) {
        return QValidator::Intermediate;
    }
    // 这里判断超过位数了，变成无效的参数
    int intValue = s.toInt();
    if (intValue > top() || intValue < bottom())
        return QIntValidator::Invalid;

    return QIntValidator::validate(s, n);
}

void CustomIntValidator::fixup(QString &input) const
{
    // 这里要做个判断，如果超过最大值取最大值 ，低于最小值取最小值，例如1转换为20， 333转换为100
    int intValue = input.toInt();
    if (intValue < bottom())
        input = QString("%1").arg(bottom());
}

QValidator::State CustomDoubleValidator::validate(QString &s, int &) const
{
    // 这里判断超过位数了，"0" , "0." , "0.0"都表示输入未完成
    if (s.isEmpty() || s == "0" || s == "0." || s == "0.0") {
        return QValidator::Intermediate;
    }

    int dotPos = s.indexOf(".");
    if (dotPos > 0) {
        int offset = s.length() - dotPos - 1;
        if (s.right(offset).length() > decimals()) {
            return QValidator::Invalid;
        }
    }

    double value = s.toDouble();
    if (value > top() || value < bottom()) {
        return QValidator::Invalid;
    }

    return QValidator::Acceptable;
}

void CustomDoubleValidator::fixup(QString &input) const
{
    // 这里要做个判断，低于最小值取最小值，例如0.01转换为0.02，
    double value = input.toDouble();
    if (value < bottom())
        input = QString("%1").arg(bottom());
}

IPAddressLineEdit::IPAddressLineEdit(QWidget *parent)
    : QLineEdit(parent)
    , m_l1(new QLineEdit(this))
    , m_l2(new QLineEdit(this))
    , m_l3(new QLineEdit(this))
    , m_l4(new QLineEdit(this))
{
    this->setStyleSheet("background-color:white;");

    initForEdit(m_l1);
    initForEdit(m_l2);
    initForEdit(m_l3);
    initForEdit(m_l4);

    QVBoxLayout *verticalLayout = new QVBoxLayout(this);
    verticalLayout->setContentsMargins(0, 0, 0, 0);
    verticalLayout->setSpacing(0);

    auto frame = new QFrame(parent);
    verticalLayout->addWidget(frame);

    auto layout = new QHBoxLayout(frame);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->addWidget(m_l1, 1);
    layout->addWidget(new QLabel(".", this));
    layout->addWidget(m_l2, 1);
    layout->addWidget(new QLabel(".", this));
    layout->addWidget(m_l3, 1);
    layout->addWidget(new QLabel(".", this));
    layout->addWidget(m_l4, 1);
}

QString IPAddressLineEdit::textIP4() const
{
    QString addr;
    addr.append(m_l1->text().isEmpty() ? "0" : m_l1->text());
    addr.append(".");
    addr.append(m_l2->text().isEmpty() ? "0" : m_l2->text());
    addr.append(".");
    addr.append(m_l3->text().isEmpty() ? "0" : m_l3->text());
    addr.append(".");
    addr.append(m_l4->text().isEmpty() ? "0" : m_l4->text());
    return addr;
}

void IPAddressLineEdit::setTextIP4(const QString &addr)
{
#ifdef QT_NETWORK_LIB
    QHostAddress ip4;
    if (!MyHelper::isIPAddress(addr, ip4)) {
        return;
    }
#else
    if (!MyHelper::isIPAddress(addr)) {
        return;
    }
#endif

    QStringList ips = addr.split(".");
    Q_ASSERT_X(ips.size() == 4, "IPAddressLineEdit::setTextIP4",
               "IP地址应该是4个数字！");
    m_l1->setText(ips.at(0));
    m_l2->setText(ips.at(1));
    m_l3->setText(ips.at(2));
    m_l4->setText(ips.at(3));

    this->update();
}

void IPAddressLineEdit::setReadOnly(bool isReadOnly)
{
    m_l1->setReadOnly(isReadOnly);
    m_l2->setReadOnly(isReadOnly);
    m_l3->setReadOnly(isReadOnly);
    m_l4->setReadOnly(isReadOnly);
}

void IPAddressLineEdit::initForEdit(QLineEdit *edit)
{
    edit->setStyleSheet("border: none");
    // edit->setFrame(false);
    edit->setAlignment(Qt::AlignCenter);
    edit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    // 设置整数验证器，允许的数值范围是0-255
    edit->setValidator(new QRegularExpressionValidator(QRegularExpression("^(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)$"), this));

    edit->installEventFilter(this);
    connect(edit, &QLineEdit::textChanged, this, &IPAddressLineEdit::textChanged);
    connect(edit, &QLineEdit::textChanged, this, [=](const QString &text) {
        //判断当前是否输入完成一个网段,是的话则自动移动到下一个输入框
        if (text.length() == 3) {
            this->focusNextChild();
        }
    });
}

bool IPAddressLineEdit::eventFilter(QObject *object, QEvent *event)
{
    if (event->type() == QEvent::KeyPress) {
        QKeyEvent *keyevent = static_cast<QKeyEvent*>(event);
        // 如果当前按下了小数点则移动焦点到下一个输入框
        if(keyevent->key() == Qt::Key_Period) {
            this->focusNextChild();
        }
        // 如果按下了退格键并且当前文本框已经没有了内容则焦点往前移
        else if (keyevent->key() == Qt::Key_Backspace) {
            if (qobject_cast<QLineEdit*>(object)->text().length() <= 1) {
                this->focusNextPrevChild(false);
            }
        } else if(keyevent->matches(QKeySequence::Paste)) {
            QString clip = QApplication::clipboard()->text(QClipboard::Clipboard);
            if(clip.split(".").size() == 4) {
                setTextIP4(clip);
            }
        } else {}
    }

    return QLineEdit::eventFilter(object, event);
}

VirtualNumKeyboard::VirtualNumKeyboard(QWidget *parent)
    : CDialog(parent)
    , m_txtVal(new QLineEdit(this))
    , m_labMinVal(new QLabel(this))
    , m_labMaxVal(new QLabel(this))
    , m_btnDel(new QPushButton("del", this))
{
    Q_ASSERT_X(parent, "VirtualNumKeyboard()",
               "VirtualNumKeyboard类不允许父窗体为空");

#ifdef USING_ONLY_CHINESE
    setWindowTitle("数字键盘");
#else
    setWindowTitle(tr("Virtual Keyboard"));
#endif // USING_ONLY_CHINESE
    VirtualNumKeyboard::initializeComponent();

    // 设置默认值和默认阈值
    setValue(0);
    setRange(0, 100);
}

void VirtualNumKeyboard::updateValue(const QString &btn_txt)
{
    if (m_txtVal->text().contains(".")) {
        // 不允许输入两个小数点
        if (btn_txt == ".") {
            return;
        }

        // 小数点后精确到12位
        auto ptNum = m_txtVal->text().length() - m_txtVal->text().indexOf('.');
        if (ptNum >= 12) {
            return;
        }
    }

    auto newStr = m_txtVal->text().append(btn_txt);
    // 避免数字无限大,输入无意义
    if (newStr.toLongLong() > ULONG_MAX) {
        return;
    }

    if (newStr.length() >= 2) {
        // 去除首位为0的整数
        if (newStr.startsWith("0") && !newStr.startsWith("0.")) {
            newStr = newStr.mid(1);
        }
        // 去除首位两个0的输入
        if (newStr.startsWith("00")) {
            newStr = newStr.mid(1);
        }
    }
    m_txtVal->setText(newStr);
}

void VirtualNumKeyboard::initializeComponent()
{    
    // 自我初始化
    CDialog::initializeComponent();

    auto frame = new QWidget(this);
    addTitleBar(frame);

    m_txtVal->setReadOnly(true);
    m_labMinVal->setStyleSheet("color:red");
    m_labMaxVal->setStyleSheet("color:red");

    auto layout = new QGridLayout();
    layout->setContentsMargins(0, 0, 0, 0);
    layout->setSpacing(3);
    layout->addWidget(m_txtVal, 0, 0, 1, 4);
    int k = 1;
    for (int i = 0; i < 3; ++i) {
        for (int j = 0; j < 3; ++j) {
            auto numBtn = new QPushButton(QString::number(k), this);
            layout->addWidget(numBtn, i + 1, j);
            connect(numBtn, &QPushButton::clicked, this, [=]() {
                updateValue(numBtn->text());
            });

            ++k;
        }
    }

    // 小数点
    auto numBtn = new QPushButton(".", this);
    layout->addWidget(numBtn, 4, 0);
    connect(numBtn, &QPushButton::clicked, this, [=]() {
        updateValue(numBtn->text());
    });
    // 数字0
    numBtn = new QPushButton("0", this);
    layout->addWidget(numBtn, 4, 1);
    connect(numBtn, &QPushButton::clicked, this, [=]() {
        updateValue(numBtn->text());
    });
    // 退格键
    layout->addWidget(m_btnDel, 4, 2);
    connect(m_btnDel, &QPushButton::clicked, this, [=]() {
        if (!m_txtVal->text().isEmpty()) {
            // 如果是非数字,可能是越限了,直接赋0
            bool isNum = false;
            auto ddd = m_txtVal->text().toDouble(&isNum);
            if (!isNum || qIsInf(ddd) || qIsNaN(ddd)) {
                m_txtVal->setText("0");
                return;
            }
            // 如果是最后一个数字,退格后则为0
            if (m_txtVal->text().length() == 1) {
                m_txtVal->setText("0");
                return;
            }

            auto tmp = m_txtVal->text().left(m_txtVal->text().length() - 1);
            m_txtVal->setText(tmp);
        }
    });

    auto layout2 = new QGridLayout();
    layout2->addWidget(new QLabel(tr("MinVal:"), this), 0, 0);
    layout2->addWidget(m_labMinVal, 0, 1);
    layout2->addWidget(new QLabel(tr("MaxVal:"), this), 1, 0);
    layout2->addWidget(m_labMaxVal, 1, 1);

    auto btnOK = new QPushButton(tr("OK"), this);
    btnOK->setStyleSheet("background-color: rgb(85, 170, 255)");
    layout2->addWidget(btnOK, 2, 0, 1, 2);
    connect(btnOK, &QPushButton::clicked, this, [=]() {
        auto currVal = m_txtVal->text().toDouble();
        if (currVal >= m_labMinVal->text().toDouble() && currVal <= m_labMaxVal->text().toDouble()) {
            m_val = currVal;
        }
        this->close();
    });
    btnOK->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    auto btnCancle = new QPushButton(tr("Cancle"), this);
    btnCancle->setStyleSheet("background-color: rgb(255, 175, 164)");
    layout2->addWidget(btnCancle, 3, 0, 1, 2);
    connect(btnCancle, &QPushButton::clicked, this, &VirtualNumKeyboard::close);

    layout2->setRowStretch(0, 1);
    layout2->setRowStretch(1, 1);
    layout2->setRowStretch(2, 1);
    layout2->setRowStretch(3, 1);
    layout->addLayout(layout2, 1, 3, 4, 1);

    // 设置自增长
    layout->setRowStretch(0, 0);
    layout->setRowStretch(1, 1);
    layout->setRowStretch(2, 1);
    layout->setRowStretch(3, 1);
    layout->setRowStretch(4, 1);
    layout->setColumnStretch(0, 1);
    layout->setColumnStretch(1, 1);
    layout->setColumnStretch(2, 1);
    frame->setLayout(layout);

    auto numBtnLst = frame->findChildren<QPushButton*>();
    foreach (auto &btn, numBtnLst) {
        btn->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    }
    this->setMinimumHeight(260);
}
